package accounts;

import database.DatabaseManager;
import java.sql.SQLException;
import stocks.Share;
import java.util.Hashtable;

/**
 * the Transaction class stores all relevant information for one particular transaction
 * made by customer, it is not possible to change the parameters once they are set
 * @author Mike
 */

public class Transaction {
    private static int counter = 0;
    public final TType type;
    public final Account accountTo;
    public final Account accountFrom;
    public final float amount;
    public final Hashtable<Share,Integer> shares;
    public final int id;

    /**
     * constrocutor will create a new transaction and apply all changes
     * @param builder the builder containing the arguments for the transaction
     */
    private Transaction (Builder builder) {
        if (builder == null) {
            throw new IllegalArgumentException();
        }
        id = counter++;
        shares = builder.shares;
        accountTo = builder.accountTo;
        accountFrom = builder.accountFrom;
        type = builder.type;
        amount = builder.amount;

        if (type == TType.deposit) {
          ((CheckingAccount)accountTo).balance += amount;
          accountTo.addTransaction(this);
        }
        else if (type == TType.withdrawl) {
          ((CheckingAccount)accountFrom).balance -= amount;
          accountFrom.addTransaction(this);
        }
        else if (type == TType.transfer) {
            ((CheckingAccount)accountTo).balance += amount;
            ((CheckingAccount)accountFrom).balance -= amount;
            accountTo.addTransaction(this);
            accountFrom.addTransaction(this);
        }
        else if (type == TType.stockBuy) {
            //shares are handled directly in the buyShares methode
            ((CheckingAccount)accountFrom).balance -= amount;
            accountTo.addTransaction(this);
            accountFrom.addTransaction(this);
        }
        else {
            //shares are handled directly in the sellShares methode
            accountTo.addTransaction(this);
            accountFrom.addTransaction(this);
        }

        try {
            //we use transaction to ensure that everything is done or nothing
            DatabaseManager db = DatabaseManager.getInstance();
            db.update("start transaction");
            int accTo = 0;
            int accFrom = 0;
            if (accountTo != null) {
                accTo = accountTo.getAccountNo();
                db.update("UPDATE account SET Balance = Balance+"+amount+" WHERE AccountNumber = "+accountTo.getAccountNo());
            }
            if (accountFrom != null) {
                accFrom = accountFrom.getAccountNo();
                db.update("UPDATE account SET Balance = Balance-"+amount+" WHERE AccountNumber = "+accountFrom.getAccountNo());
            }
            db.update("INSERT INTO transactions VALUES(DEFAULT," + accTo + "," + accFrom + "," + amount + ")");
            db.update("commit");
        }
        catch (SQLException e) {
            System.out.println("TRANSACTION FAILED!");
            System.exit(-1);
        }
    }
    /**
     * returns a new builder to generate a new transaction
     * @return the builder
     */
    public static Builder getBuilder() {
        return new Transaction.Builder();
    }
    /**
     * inner class for builder pattern
     */
    public static class Builder {
        private TType type;
        private Account accountTo;
        private Account accountFrom;
        private float amount;
        private Hashtable<Share,Integer> shares;

       /**
        * constructor for builder pattern
        */
        public Builder() {
            shares = new Hashtable<Share,Integer>();
            type = TType.transfer;
            amount = 0;
        }
        /**
         * sets the accounts for the builder
         * @param from account from which money is taken
         * @param to account to which money is transfered
         * @return the builder
         */
        public Builder setAccounts (Account from, Account to) {
            accountTo = to;
            accountFrom = from;
            return this;
        }
        /**
         * sets the amount of money
         * @param _amount the amount of money
         * @return the builder
         */
        public Builder setAmount (float _amount) {
            amount = _amount;
            return this;
        }
        /**
         * 
         * @param _type
         * @return
         */
        public Builder setType (TType _type) {
            type = _type;
            return this;
        }
        /**
         * 
         * @param share
         * @param amount
         * @return
         */
        public Builder addShare (Share share, int amount) {
            shares.put(share,amount);
            return this;
        }
        /**
         * builds the transaction
         * @return
         */
        public Transaction build () {
            //other integrity conditions could be added here
            if ((type == TType.stockBuy || type == TType.stockSell) && (shares.size() == 0)) {
                    throw new IllegalArgumentException();
            }
            return new Transaction(this);
        }
    }
}

